use darling::FromDeriveInput;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput};

pub mod can;
pub mod dma;
pub mod gpio;
mod rcc;
pub mod spi;
pub mod tim_pwm;
pub mod time_delay;
pub mod uart;
pub mod tim_pwm2;

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(periph))]
pub struct RegConfig {
    periph: String,
}

pub fn reg_build(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let config = RegConfig::from_derive_input(&input).unwrap();
    let name = &input.ident;
    let low = format_ident!("{}", config.periph.to_lowercase());
    let up = format_ident!("{}", config.periph.to_uppercase());

    let expand = quote! {

        impl ::core::ops::Deref for #name {
            type Target = stm32f1::stm32f103::#low::RegisterBlock;
            fn deref(&self) -> &Self::Target {
                unsafe { &* stm32f1::stm32f103::#up::PTR }
            }
        }

    };
    expand.into()
}
#[derive(Debug)]
pub struct DmaInfo {
    pub dma: u8,
    pub ch: u8,
    pub name: String,
}

impl DmaInfo {

    pub fn with_name(name: &str) -> Self {
        if let Some(info) = Self::find_dma1(name) {
            return info;
        }
        Self::find_dma2(name).unwrap()
    }

    fn find_dma1(name: &str) -> Option<Self> {
        let ch = match name.to_uppercase().as_str() {
            "ADC1" | "TIM2_CH3" | "TIM4_CH1" => Some(1),
            "SPI1_RX" | "USART3_TX" | "TIM1_CH1" | "TIM2_UP" | "TIM3_CH3" => Some(2),
            "SPI1_TX" | "USART3_RX" | "TIM1_CH2" | "TIM3_CH4" | "TIM3_UP" => Some(3),
            "SPI2_RX" | "I2S2_RX" | "USART1_TX" | "I2C2_TX" | "TIM1_TX4" | "TIM1_TRIG"
            | "TIM1_COM" | "TIM4_CH2" => Some(4),
            "SPI2_TX" | "I2S2_TX" | "USART1_RX" | "I2C2_RX" | "TIM1_UP" | "TIM2_CH1"
            | "TIM4_CH3" => Some(5),
            "USART2_RX" | "I2C1_TX" | "TIM1_CH3" | "TIM3_CH1" | "TIM3_TRIG" => Some(6),
            "USART2_TX" | "I2C1_RX" | "TIM2_CH2" | "TIM2_CH4" | "TIM4_UP" => Some(7),
            _ => None,
        };
        let ch = ch?;
        let info = DmaInfo {
            dma: 1,
            ch,
            name: name.to_lowercase(),
        };
        Some(info)
    }

    fn find_dma2(name: &str) -> Option<Self> {
        let ch = match name.to_uppercase().as_str() {
            "TIM5_CH4" | "TIM5_TRIG" | "TIM8_CH3" | "SPI3_RX" | "I2S3_RX" => Some(1),
            "TIM8_CH4" | "TIM8_TRIG" | "TIM8_COM" | "TIM5_CH3" | "TIM5_UP" |
            "SPI3_TX" | "I2S3_TX" => Some(2),
            "TIM8_CH1" | "UART4_RX" | "TIM6_UP" | "DAC_CHANNEL1" => Some(3),
            "TIM5_CH2" | "SDIO" | "TIM7_UP" | "DAC_CHANNEL2" => Some(4),
            "ADC3" | "TIM8_CH2" | "TIM5_CH1" | "UART4_TX" => Some(5),
            _ => None
        };
        let ch = ch?;
        let info = DmaInfo {
            dma: 2,
            ch,
            name: name.to_lowercase(),
        };
        Some(info)
    }
}
